﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Ex03.GarageManagementSystem.ConsoleUI
{
    /// <summary>
    /// the UI of managing a garage
    /// </summary>
    internal static class GarageUI
    {
        private const int k_MenuFirstChoice = 1;
        private const int k_MenuLastChoice = 8;
        
        /// <summary>
        /// this function print the main menu of the UI and commit the sutiable function
        /// </summary>
        /// <param name="io_Garage">the Garage to manag</param>
        /// <returns>if the uset want to continue</returns>
        public static bool StartGarageMenu(GarageLogic.Garage io_Garage)
        {
            bool continueGarage = true;
            printChoiceMenu();
            int userChoiceFromMenu = getChoiseIntByMessage("Choose operation number " + k_MenuFirstChoice + " - " + k_MenuLastChoice + ": ");

            switch (userChoiceFromMenu)
            {
                case 1:
                    InsertVehicleToGarage(ref io_Garage);
                    break;
                case 2:
                    showListOfLisenceNumberByTreatmeantState(io_Garage);
                    break;
                case 3:
                    changeVehicleState(io_Garage);
                    break;
                case 4:
                    pumpVehicleWheelsToMax(io_Garage);
                    break;
                case 5:
                    fuelFueledPowerdVehicle(io_Garage);
                    break;
                case 6:
                    chargeElectricVehicle(io_Garage);
                    break;
                case 7:
                    showVehicleInfoByLicense(io_Garage);
                    break;
                case 8:
                    continueGarage = false;
                    break;
            }

            return continueGarage;
        }

        private static void printChoiceMenu()
        {
            string menuString = string.Empty;
            System.Console.Clear();

            menuString = string.Format(
@"
1. Enter vehicle to grage.
2. Show list of lisence numbers by garge state.
3. Change vehicle state.
4. Pump wheels to maximum.
5. Fuel vehicle.
6. Charge vehicle.
7. show full info of vehicle by lisence number.
8. Exit.");
            System.Console.WriteLine(menuString);
        }

        private static void InsertVehicleToGarage(ref GarageLogic.Garage io_Garage)
        {
            GarageLogic.eStageOfTreatment k_defaultVehicleInGargeState = GarageLogic.eStageOfTreatment.InRepair;
            string stringLisenceNumber = string.Empty;
            bool vehicleExistResult = false;

            stringLisenceNumber = getSimpleStringInputByMessage("Enter lisence number: ");
            vehicleExistResult = io_Garage.IsVehicleExist(stringLisenceNumber);

            if (vehicleExistResult)
            {
                io_Garage.ChangeVehicleState(stringLisenceNumber, k_defaultVehicleInGargeState);
                System.Console.WriteLine("Vehicle allready exist, state was changed");
            }
            else 
            {
                InputFromUserNewVehicle(ref io_Garage, stringLisenceNumber);
                System.Console.WriteLine("Vehicle inserted successfully");
            }

            Console.ReadLine();
        }

        private static void InputFromUserNewVehicle(ref GarageLogic.Garage io_Garage, string i_StringLisenceNumber)
        {
            string modelNameForVehicle = getSimpleStringInputByMessage("Enter model name: ");
            string wheelManufacturerName = getSimpleStringInputByMessage("Enter wheel manufacturer name: ");
            GarageLogic.eVehicleType vehicleType = getEnumStringByMessageAndEnumType<GarageLogic.eVehicleType>("Enter vehicle type from:");
            bool isMatchedEnergySourceToVehicleType = false;
            const bool v_EnergySourceMatchedToVehicleType = true;
            bool continueToNextStep = false;
            string ownerName = string.Empty;
            string ownerPhoneNumber = string.Empty;

            while (!isMatchedEnergySourceToVehicleType)
            {
                GarageLogic.eEnergyType energySourceType = getEnumStringByMessageAndEnumType<GarageLogic.eEnergyType>("Enter energy type from:");
                try
                {
                    io_Garage.CreateNewVehicleInGarage(vehicleType, energySourceType, modelNameForVehicle, i_StringLisenceNumber, wheelManufacturerName);
                    isMatchedEnergySourceToVehicleType = v_EnergySourceMatchedToVehicleType;
                }
                catch (ArgumentException e)
                {
                    System.Console.WriteLine(e.Message);
                    isMatchedEnergySourceToVehicleType = !v_EnergySourceMatchedToVehicleType;
                }
            }

            while (!continueToNextStep)
            {
                try
                {
                    getVehicleSpecificProperties(ref io_Garage, i_StringLisenceNumber);
                    continueToNextStep = true;
                }
                catch (FormatException ex)
                {
                    Console.WriteLine(ex.Message);
                    continueToNextStep = false;
                }
                catch (ArgumentException ex)
                {
                    Console.WriteLine(ex.Message);
                    continueToNextStep = false;
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    continueToNextStep = false;
                }
            }

            ownerName = getSimpleStringInputByMessage("Enter owner name: ");
            ownerPhoneNumber = getSimpleStringInputByMessage("Enter owner phone number: ");
            io_Garage.SetOwnerNameAndPhoneToVehicleInGarage(i_StringLisenceNumber, ownerName, ownerPhoneNumber);            
        }

        private static void getVehicleSpecificProperties(ref GarageLogic.Garage io_Garage, string i_StringLisenceNumber)
        {
            List<string> listOfSpecificVehiclePropertiesMessages = io_Garage.GetPropertiesByVehicleLicense(i_StringLisenceNumber);
            List<string> listOfSpecificVehiclePropertiesFromUser = new List<string>();

            foreach (string propertyMessage in listOfSpecificVehiclePropertiesMessages)
            {
                listOfSpecificVehiclePropertiesFromUser.Add(getSimpleStringInputByMessage(propertyMessage));
            }           

            io_Garage.SetPropertiesByVehicleLicense(i_StringLisenceNumber, listOfSpecificVehiclePropertiesFromUser);
        }

        private static void showListOfLisenceNumberByTreatmeantState(GarageLogic.Garage i_Garage)
        {
            const int k_EmptyList = 0;
            List<string> listOfLisenceNumberByTreatmeantState = getListOfVehiclesByStateFromGarage("Enter trearmeant state type from:", i_Garage);
            if (listOfLisenceNumberByTreatmeantState.Count == k_EmptyList)
            {
                System.Console.WriteLine("No Vehicles :-(");
            }
            else
            {
                System.Console.WriteLine("The vehicles:\n");
                foreach (string lisenceNumber in listOfLisenceNumberByTreatmeantState)
                {
                    System.Console.WriteLine(lisenceNumber);
                }
            }

            System.Console.ReadLine();
        }

        private static List<string> getListOfVehiclesByStateFromGarage(string i_MessageForUser, GarageLogic.Garage i_Garage)
        {
            const bool v_CaseSensitive = true;
            const bool v_AllVehicles = true;
            const string k_AllVehicles = "ALL";
            StringBuilder messageForUser = new StringBuilder();
            string[] names = Enum.GetNames(typeof(GarageLogic.eStageOfTreatment));
            string userInputString = string.Empty;
            bool enumParseResult = false;
            bool returnAllVehicles = false;
            GarageLogic.eStageOfTreatment resultInputType = 0;
            List<string> vehiclesFromGarage = new List<string>();

            messageForUser.AppendLine(i_MessageForUser);
            messageForUser.Append(listToStringWithNumbers(names));
            messageForUser.Append("ALL");
            while (!enumParseResult)
            {
                System.Console.WriteLine(messageForUser);
                userInputString = System.Console.ReadLine();
                if (userInputString.Equals(names.Length.ToString()) || userInputString.Equals(k_AllVehicles))
                {
                    enumParseResult = v_AllVehicles;
                    returnAllVehicles = v_AllVehicles;
                }
                else
                {
                    enumParseResult = Enum.TryParse(userInputString, v_CaseSensitive, out resultInputType);
                    enumParseResult = Enum.IsDefined(typeof(GarageLogic.eStageOfTreatment), resultInputType);
                }
            }

            vehiclesFromGarage = i_Garage.GetListOfLisenceNumberByTreatmeantState(resultInputType, returnAllVehicles);

            return vehiclesFromGarage;
        }

        private static void changeVehicleState(GarageLogic.Garage i_Garage)
        {
            string stringLisenceNumber = getSimpleStringInputByMessage("Enter lisence number: ");

            if (i_Garage.IsVehicleExist(stringLisenceNumber))
            {
                GarageLogic.eStageOfTreatment userTreatmeantState = getEnumStringByMessageAndEnumType<GarageLogic.eStageOfTreatment>("Enter trearmeant state type from:");
                i_Garage.ChangeVehicleState(stringLisenceNumber, userTreatmeantState);
                System.Console.WriteLine("Treatment state was changed to " + userTreatmeantState);
            }
            else 
            {
                Console.WriteLine("Lisence number does not exist in the garage");
            }
            
            System.Console.ReadLine();
        }

        private static void pumpVehicleWheelsToMax(GarageLogic.Garage i_Garage)
        {
            string stringLisenceNumber = getSimpleStringInputByMessage("Enter lisence number: ");

            if (i_Garage.IsVehicleExist(stringLisenceNumber))
            {
                i_Garage.PumpVehicleWheelsToMax(stringLisenceNumber);
                Console.WriteLine("All wheels was pumped to max!"); 
            }
            else
            {
                Console.WriteLine("Lisence number does not exist in the garage");
            }

            System.Console.ReadLine();
        }

        private static void fuelFueledPowerdVehicle(GarageLogic.Garage i_Garage)
        {
            string stringLisenceNumber = getSimpleStringInputByMessage("Enter lisence number: ");

            if (i_Garage.IsVehicleExist(stringLisenceNumber))
            {
                float howMuchFuelToFuel = getSimpleFloatInputByMessage("Enter how much fuel you want to fill: ");
                GarageLogic.eFuelType typeOfFuel = getEnumStringByMessageAndEnumType<GarageLogic.eFuelType>("Enter full type from:");
               
                try
                {
                    i_Garage.FuelFueledPowerdVehicle(stringLisenceNumber, howMuchFuelToFuel, typeOfFuel);
                    System.Console.WriteLine("{0} liters were add to the tank", howMuchFuelToFuel);
                }
                catch (FormatException ex)
                {
                    Console.WriteLine(ex.Message);
                }
                catch (ArgumentException ex)
                {
                    Console.WriteLine(ex.Message);
                }
                catch (GarageLogic.ValueOutOfRangeException ex)
                {
                    Console.WriteLine("Fuel must be between {0} to {1} liters", ex.MainValue, ex.MaxValue);                    
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
            else 
            {
                Console.WriteLine("Lisence number does not exist in the garage");
            }

            System.Console.ReadLine();
        }

        private static void chargeElectricVehicle(GarageLogic.Garage i_Garage)
        {            
            string stringLisenceNumber = getSimpleStringInputByMessage("Enter lisence number: ");

            if (i_Garage.IsVehicleExist(stringLisenceNumber))
            {
                float minutesOfChargeTime = getSimpleFloatInputByMessage("Enter number of minutes to charge: ");
                try
                {
                    i_Garage.ChargeBattery(stringLisenceNumber, minutesOfChargeTime);
                    System.Console.WriteLine("The battery was charged with {0} minutes", minutesOfChargeTime);
                }              
                catch (ArgumentException ex)
                {
                    Console.WriteLine(ex.Message);
                }
                catch (GarageLogic.ValueOutOfRangeException ex)
                {                    
                    Console.WriteLine("Electric time must be between {0} to {1} hours", ex.MainValue, ex.MaxValue);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }                                
            }
            else 
            {
                Console.WriteLine("Lisence number does not exist in the garage");
            }

            System.Console.ReadLine();
        }

        private static void showVehicleInfoByLicense(GarageLogic.Garage i_Garage)
        {
            string stringLisenceNumber = getSimpleStringInputByMessage("Enter lisence number: ");

            if (i_Garage.IsVehicleExist(stringLisenceNumber))
            {
                Console.WriteLine(i_Garage.PrintInfoOfVehicle(stringLisenceNumber));
            }
            else
            {
                Console.WriteLine("Lisence number does not exist in the garage");
            }

            System.Console.ReadLine();
        }

        private static int getChoiseIntByMessage(string i_MessageForUser)
        {
            string userInputString = string.Empty;

            int userInputInt = 0;
            bool realInt = false;

            while (!realInt)
            {
                userInputString = getSimpleStringInputByMessage(i_MessageForUser);
                realInt = int.TryParse(userInputString, out userInputInt);
                if (realInt)
                {
                    realInt = userInputInt <= k_MenuLastChoice && userInputInt >= k_MenuFirstChoice;
                }
            }

            return userInputInt;
        }

        private static float getSimpleFloatInputByMessage(string i_MessageForUser)
        {
            string userInputString = string.Empty;

            float userInputFloat = 0;
            bool realFloat = false;

            while (!realFloat)
            {
                userInputString = getSimpleStringInputByMessage(i_MessageForUser);
                realFloat = float.TryParse(userInputString, out userInputFloat);
            }

            return userInputFloat;
        }

        private static string getSimpleStringInputByMessage(string i_MessageForUser)
        {
            string userInputString = string.Empty;

            while (userInputString.Equals(string.Empty))
            {
                System.Console.WriteLine(i_MessageForUser);
                userInputString = System.Console.ReadLine();
                userInputString = userInputString.Trim();
            }

            return userInputString;
        }

        private static string buildEnumNamesString(Type i_EnumType)
        {
            StringBuilder messageForUser = new StringBuilder();
            string[] names = Enum.GetNames(i_EnumType);
            string userInputString = string.Empty;
           
            for (int i = 0; i < names.Length; i++)
            {
                messageForUser.AppendLine(names[i]);
            }

            return messageForUser.ToString();
        }
 
        private static TEnum getEnumStringByMessageAndEnumType<TEnum>(string i_MessageForUser) where TEnum : struct
        {
            StringBuilder messageForUser = new StringBuilder();
            string[] names = Enum.GetNames(typeof(TEnum));
            string userInputString = string.Empty;
            TEnum resultInputType = default(TEnum);
            bool enumParseResult = false;
            const bool v_CaseSensitive = true;
            
            messageForUser.AppendLine(i_MessageForUser); 
            messageForUser.Append(listToStringWithNumbers(names));
            while (!enumParseResult)
            {
                System.Console.WriteLine(messageForUser);
                userInputString = System.Console.ReadLine();
                enumParseResult = Enum.TryParse(userInputString, v_CaseSensitive, out resultInputType);
                enumParseResult = Enum.IsDefined(typeof(TEnum), resultInputType);
            }

            return resultInputType;
        }

        private static string listToStringWithNumbers(string[] i_ListNames)
        {
            StringBuilder messageForUser = new StringBuilder();

            for (int i = 0; i < i_ListNames.Length; i++)
            {
                messageForUser.AppendLine(i_ListNames[i]);
            }

            return messageForUser.ToString();
        }
    }
}